/*****************************************************************************
 *
 * MODULE NAME:    sys_power.c
 * PROJECT CODE:   BlueStream
 * DESCRIPTION:    Low Power Clock Activation
 * MAINTAINER:     Tom Kerwick
 * DATE:           10 December 2001
 *
 * SOURCE CONTROL: $Id: sys_power.c,v 1.12 2011/04/01 16:03:27 garyf Exp $
 *
 * LICENSE:
 *     This source code is copyright (c) 2000-2008 Ceva Inc.
 *     All rights reserved.
 *
 *****************************************************************************/


#include "sys_config.h"
#include "sys_types.h"
#include "sys_const.h"
#include "hw_delay.h"
#include "uslc_chan_ctrl.h"
#include "lslc_irq.h"
#include "sys_hal_features.h"
#include "sys_features.h"
#include "sys_power.h"
#include "lmp_timer.h"
#include "lmp_config.h"
#include "lmp_acl_container.h"
#include "bk3231s_reg.h"
#include "include.h"
#include "DeviceBtj_Header.h"
#include "driver_icu.h"// for warning messge change
#include "le_connection.h"
#include "updata_gatt_service.h"

extern t_queue BTQueues[];
extern u_int32  Check_Inquiry_Is_Busy(void);
extern u_int32 Get_exist_Acl_Num(void);


static boolean syspwr_low_power_mode_active_;
u_int8 LowFrequencyOscillatorAvailable = 0;

/* can be increased if CPU is slown down and execution takes time */
#define SYS_PWR_MIN_FRAMES_TO_SLEEP      1
#define SYS_PWR_AUX_TIMER_MIN_RESOLUTION 4

volatile u_int8 b_26M_colsed=0;
extern volatile u_int8 b_gpio_intr;
extern void ARM_Enter_Light_mode(void);
extern u_int8 enter_sleep_flag,hid_mode_flag;
extern u_int16 timer_tick2;

void sys_idle()
{
    //uint32 flash_dat;// for warning messge change
#if(CEVA_CLK_16M_TO_32K)
	
	//REG_AHB0_ICU_CEVA_CLKCON=0x01;
    b_26M_colsed=1;
    set_flash_clk(8);    
    flash_set_line_mode(1); 
    
    Set_CPU_Speed_16M();
    delay_us(2);
    key_wakeup_enable();
    
    BK3000_XVR_REG_0x09 = 0x19407D01;
    REG_AHB0_DCO16M_PWD 	 = 0x1 ;

	REG_AHB0_ICU_DIGITAL_PWD = SET_CB_BAIS_PWD +SET_CLK48M_PWD +
								SET_HP_LDO_PWD + SET_REF1V_PWD;//SET_CLK16M_PWD 

    delay_us(10);
    
    REG_AHB0_ICU_CORECLKCON = 0x01;
	
#else

	REG_AHB0_DCO16M_PWD  = 0x0 ;
    BK3000_XVR_REG_0x0A = XVR_ANALOG_REG_BAK[10] = 0xC0016209;
    delay_us(10);

    set_flash_clk(8);    
    flash_set_line_mode(1); 
    
    Set_CPU_Speed_16M();
    delay_us(2);
    
    key_wakeup_enable();
    b_26M_colsed=1;
    
    REG_AHB0_ICU_CEVA_CLKCON=0x01;
    REG_AHB0_ICU_DIGITAL_PWD = 0x80+SET_CB_BAIS_PWD +SET_CLK48M_PWD +
                               SET_HP_LDO_PWD + SET_REF1V_PWD+SET_CLK16M_PWD ;
    
    DEBUG_MSG(0xA0);
    
    REG_AHB0_ICU_CLKSRCSEL  = 0X00 ;
    delay_us(1);
    REG_AHB0_DCO16M_PWD 	= 0x01 ;
    
    REG_AHB0_ICU_CORECLKCON = 0x01;
    delay_us(1);
    
#endif	
   
}

void sys_wakeup(void)
{
     if(b_26M_colsed)
    { 
    	b_26M_colsed=0;
#if(CEVA_CLK_16M_TO_32K)
        //REG_AHB0_ICU_CEVA_CLKCON=0x00;
        BK3000_XVR_REG_0x09 = 0x19407D00;
        
        delay_us(50);
        Set_CPU_PLL_clk(1); 
        
        //flash_set_line_mode(4);
        set_flash_clk(1);
#else
		//flash_set_line_mode(1);
        while(!(flash_read_qe()&0x02));
        //flash_read_data(linkkey_buff,0x40000,22);
        
        //flash_set_line_mode(4);
        
        set_flash_clk(1);
        
#if( CEVA_CLK_RC32K )   
        //CLK32K_AutoCali_Enable(0);
#endif

#endif
    }

}

void Enter_Deep_sleep(void)
{
   
    set_gpio_deep_sleep();
    set_flash_clk(8);    
    flash_set_line_mode(1); 
    Set_CPU_Speed_16M();

    REG_AHB0_ICU_ANALOG_MODE &= ~(0x1<<18);// Enable Test Mode of pwdSARADCldo1V
    delay_us(10);
    REG_AHB0_ICU_ANALOG_MODE &= ~(0x1<<21);// Enable Test Mode of pwd48MPLL1V
    delay_us(20);
    BK3000_XVR_REG_0x09  = 0x18250000;
    delay_us(20);

    REG_AHB0_ICU_ADCCLKCON  = 0x1 ;
    REG_AHB0_ICU_UARTCLKCON = 0x1 ;
    REG_AHB0_ICU_I2CCLKCON  = 0x1 ;
    REG_AHB0_ICU_SPICLKCON  = 0x1 ;
    REG_AHB0_ICU_CEVA_CLKCON= 0x1 ;
    REG_AHB0_ICU_WDTCLKCON  = 0x1 ;
    REG_AHB0_ICU_BK24CLKCON = 0x1 ;
    REG_AHB0_ICU_LPO_CLKCON = 0x1 ;
    REG_AHB0_ICU_PWM0CLKCON = 0x1 ;
    REG_AHB0_ICU_PWM1CLKCON = 0x1 ;  
    REG_AHB0_ICU_PWM2CLKCON = 0x1 ;  
    REG_AHB0_ICU_PWM3CLKCON = 0x1 ;  
    REG_AHB0_ICU_PWM4CLKCON = 0x1 ;

    REG_AHB0_ICU_SLEEP_TIME = 0x0 ; // Disable 32KHz ROSC Auto-Calibration as it is not used.

    
    BK3000_XVR_REG_0x0A  = (XVR_ANALOG_REG_BAK[10] | 0x1);
    delay_us(10);
    REG_AHB0_ICU_INT_ENABLE=0;
    REG_AHB0_ICU_IRQ_ENABLE=0;
    
    REG_AHB0_RSTNREG_LATCH=0x1;
    delay_us(10);
    REG_AHB0_DCO16M_PWD     = 0x0 ;
    delay_us(20);

    // Write 0x3231 to Deep Sleep Word Register to enable deep-sleep
    REG_AHB0_ICU_DIGITAL_PWD = SET_CLK16M_PWD + SET_CLK48M_PWD + SET_CB_BAIS_PWD +
                                       SET_HP_LDO_PWD + SET_REF1V_PWD;
    //REG_AHB0_ICU_DEEP_SLEEP1 &= 0x0000FFFF  ;
    REG_AHB0_ICU_DEEP_SLEEP1 |=( 0x3231<<16);
    delay_us(10);
    while(1);
}

/*
 * FUNCTION:   SYSpwr_Is_Available_Sleep_System()
 * PURPOSE:    Returns TRUE if system is available to sleep.
 */
int SYSpwr_Is_Available_Sleep_System(void)
{
	
        /* No need to monitor LMP_OUT, L2CAP_OUT and SCO_OUT queue
         * because once placed data there, dequeued in LC ISR
         * so that halt scheduler at this point is no problem
         * even if containing any data 
         */
         
        t_queue *q = BTQueues;
        
        /*
         * Check for L2CAP_IN_Q
         */
        q = BTQueues + L2CAP_IN_Q;      
        do
        {
                if(q->num_entries)      
                    return(0/*Not Empty*/);
                q++;
        }
        while(q != &BTQueues[L2CAP_OUT_Q]);




    if(Check_Inquiry_Is_Busy())
    {
        return 0;
    }

        /*
         * Allow sleep if timers not expired
         */
    return(!LMtmr_Timer_Expired()); 
   
}  


/*****************************************************************************
 * FUNCTION:   SYSpwr_Halt_System
 * PURPOSE:    Turn off entire chip / Deep Sleep
 ****************************************************************************/


void  SYSpwr_Halt_System(void)
{
    // may add cpu halt/sleep functionality here
    if(SYSpwr_Is_Available_Sleep_System())
    {      
        if( key_release_cnt<0xf0 && get_latency_status())
            key_release_cnt++; 
       
        sys_flag|=FLAG_BT_IN_IDLE;
        
#if(GATT_SERVICE_OTA)
        if(suspend_low_power_mode==1)
        {
            return;
        }
#endif  

#if(LOW_POWER_CONNECTION)
    	sys_idle();
    	sys_wakeup();
#endif
    }
    syspwr_low_power_mode_active_ = TRUE;
}


/*****************************************************************************
 * FUNTION:    SYSpwr_Sleep_System()
 * PURPOSE:    Turn off CPU clock / Light Sleep
 ****************************************************************************/
void SYSpwr_Sleep_System(void)
{


}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Exit_Halt_System()
 * PURPOSE:    Wake entire chip back up (re-enable any gated clocks etc.)
 ****************************************************************************/
void SYSpwr_Exit_Halt_System(void)
{
    syspwr_low_power_mode_active_ = FALSE;
}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Is_Available_Halt_System()
 * PURPOSE:    Returns TRUE if system is available to halt.
 ****************************************************************************/
int SYSpwr_Is_Available_Halt_System(void)
{
    return (syspwr_low_power_mode_active_);
}



/*****************************************************************************
 * FUNCTION:   SYSpwr_LowFrequencyOscillatorAvailable()
 * PURPOSE:    Returns TRUE if low freq oscillator is available.
 ****************************************************************************/
t_sys_lf_osc_type SYSpwr_LowFrequencyOscillatorAvailable(void)
{

    return SYS_LF_OSC_32K000;
    /*
    if(g_beken_initial_config.g_Disable_ACL_active_check_when_Sleep==0)
    {
        if(Get_Active_Acl_Num()>0)
        return SYS_LF_OSC_NONE;
    }
    else
        return (t_sys_lf_osc_type)LowFrequencyOscillatorAvailable;// for warning messge change
*/
}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Setup_Sleep_Timer()
 * PURPOSE:    Generic Function to setup sleep timer - can be implemented
 *             using any clock source capable of generating interrupt
 *
 *             this version uses AUX_TIMER
 ****************************************************************************/
static u_int32 syspwr_num_frames_to_sleep_;

extern u_int8 fix_auxtimer_bug_flag;

void SYSpwr_Setup_Sleep_Timer(u_int32 max_num_frames_to_sleep)
{
/*#define NUM_FRAMES_TO_SLEEP 0x3FF*/
#define NUM_FRAMES_TO_SLEEP (0x3FFF << 1)
#define AUX_T_SHIFT 1
/* special values: 
 * 0 - stop timer
 * 1 - minimum value
 */
/*  
    if (max_num_frames_to_sleep == 0)
    {
        HW_set_aux_timer(0);
        max_num_frames_to_sleep = 0;
    } else

*/

    HW_set_aux_tim_intr_clr(1);
    if (max_num_frames_to_sleep == 1)
    {
        HW_set_aux_timer(1);
        max_num_frames_to_sleep = 0;
    } 
    else if (max_num_frames_to_sleep > NUM_FRAMES_TO_SLEEP)
    {
        HW_set_aux_timer(NUM_FRAMES_TO_SLEEP >> AUX_T_SHIFT);
        max_num_frames_to_sleep -= NUM_FRAMES_TO_SLEEP + SYS_PWR_MIN_FRAMES_TO_SLEEP;
    }
    else
    {
        if (max_num_frames_to_sleep > (SYS_PWR_MIN_FRAMES_TO_SLEEP + SYS_PWR_AUX_TIMER_MIN_RESOLUTION))
        { 
            HW_set_aux_timer(((max_num_frames_to_sleep - SYS_PWR_MIN_FRAMES_TO_SLEEP ) >> AUX_T_SHIFT)- fix_auxtimer_bug_flag);
             fix_auxtimer_bug_flag = 0;
        }
        else
        {
            HW_set_aux_timer(SYS_PWR_AUX_TIMER_MIN_RESOLUTION>>AUX_T_SHIFT);
        }

        max_num_frames_to_sleep = 0;
    }
    syspwr_num_frames_to_sleep_ = max_num_frames_to_sleep;
}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Initialise
 * PURPOSE:    
 ****************************************************************************/
void SYSpwr_Initialise(void)
{
    syspwr_num_frames_to_sleep_ = 0;
    syspwr_low_power_mode_active_ = FALSE;

	/*
	 * Leave low power oscillator disabled until enabled by TCI.
	 */
#if(BK3000_CHIP_SELECT==BK3000_FPGA)
	LowFrequencyOscillatorAvailable = SYS_LF_OSCILLATOR_PRESENT;
#else
#ifdef BEKEN_ENABLE_LOW_POWER_MODE
#ifdef LPO_GEN_IS_16M_TO_32K
    LowFrequencyOscillatorAvailable = SYS_LF_OSCILLATOR_PRESENT;
#else
	LowFrequencyOscillatorAvailable = SYS_LF_OSC_32K768;
#endif
#else
	LowFrequencyOscillatorAvailable = SYS_LF_OSCILLATOR_PRESENT;
#endif
#endif

}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Handle_Early_Wakeup
 * PURPOSE:    
 ****************************************************************************/
void SYSpwr_Handle_Early_Wakeup(void)
{
    /* wake chip back up to check on UART status */
    //SYSpwr_Exit_Halt_System();

    /*
     * DEPLOYMENT TEAM need to put code into this if() to check for HCI transport
     * activity. If TRUE, then subsequent 'naps' for this sleep period will be aborted
     */
    if(!syspwr_num_frames_to_sleep_ || (sys_flag&FLAG_GPIO_INTERRPUT))
    {
        //SYSpwr_Exit_Halt_System();
        syspwr_low_power_mode_active_ = FALSE;
       /*
        * disable the sleep timer
        * as Setup_Sleep_Timer above uses AUX_TIMER, must disable it here
        */
       HW_set_aux_tim_intr_mask(1);
       LSLCirq_Disable_Aux_Tim_Intr();
       USLCchac_Wakeup(); /* move onto next state, wake system up... */
    }
    else
    {

        SYSpwr_Setup_Sleep_Timer(syspwr_num_frames_to_sleep_);

        SYSpwr_Halt_System();
    }     
}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Is_Low_Power_Mode_Active
 * PURPOSE:    
 ****************************************************************************/
boolean SYSpwr_Is_Low_Power_Mode_Active(void)
{
    return (syspwr_low_power_mode_active_);
}

/*****************************************************************************
 * FUNCTION:   SYSpwr_Set_LowFrequencyOscillatorAvailable_Value()
 * PURPOSE:    set if oscillator is available.
 *             Returns TRUE if request was processed.
 ****************************************************************************/
boolean SYSpwr_Set_LowFrequencyOscillatorAvailable_Value(u_int8 osc_available)
{
   if((SYS_LF_OSCILLATOR_PRESENT==0) && (osc_available==1))
        return FALSE;
    LowFrequencyOscillatorAvailable = osc_available;
    return TRUE;
}

/*****************************************************************************
 * SYSpwr_Get_Min_Frames_To_Sleep
 * minimum amount of frames that device can be put in deep sleep
 ****************************************************************************/
u_int32 SYSpwr_Get_Min_Frames_To_Sleep(void)
{
    return (SYS_PWR_MIN_FRAMES_TO_SLEEP + SYS_PWR_AUX_TIMER_MIN_RESOLUTION);

}

